package com.heima.utils.common;

import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.util.*;

/**
 * 字典树节点类，用于构建中文分词所需的前缀树结构
 * 每个节点维护子节点映射表和词语结束标记
 */
class TrieNode {
    // 子节点映射表，记录当前字符到后续字符的跳转关系
    // Key: 当前字符；Value: 对应子节点
    Map<Character, TrieNode> children;

    // 标记是否为完整词语的结束节点
    // true表示从根节点到当前节点构成完整词语
    boolean isEnd;

    /**
     * 初始化空子节点映射和结束标记
     * 构造空字典树节点，初始化HashMap存储子节点
     */
    public TrieNode() {
        this.children = new HashMap<>();
        this.isEnd = false;
    }
}

/**
 * 字典树实现类，提供词语插入和词典加载功能
 * 支持构建前缀树结构，实现O(n)复杂度的词语插入和查找
 */
class Trie {
    // 根节点，代表空字符的起始点
    TrieNode root;

    /**
     * 初始化包含空根节点的字典树
     * 创建空字典树实例，根节点无字符值
     */
    public Trie() {
        this.root = new TrieNode();
    }

    /**
     * 将指定词语插入字典树
     * 逐字符构建路径，若路径不存在则创建新节点
     * @param word 需要插入的词语，非空字符串
     */
    public void insert(String word) {
        TrieNode node = root;
        // 逐字符遍历构建树路径
        for (char c : word.toCharArray()) {
            // 如果路径不存在则创建新节点
            node.children.putIfAbsent(c, new TrieNode());
            node = node.children.get(c);
        }
        // 标记词语结束节点，表示完整词语的终止
        node.isEnd = true;
    }

    /**
     * 从指定文件加载词典数据（一行一词）
     * 按行读取并插入有效词语，支持UTF-8编码文本
     * @param filePath 词典文件路径（相对路径或绝对路径）
     * @throws IOException 文件读取异常时抛出
     */
    public void loadDictionaryByFile(String filePath) throws IOException {
        // 使用缓冲读取器高效读取文件
        try (BufferedReader reader = new BufferedReader(new FileReader(filePath))) {
            String line;
            // 逐行读取直到文件末尾
            while ((line = reader.readLine()) != null) {
                String word = line.trim();
                // 忽略空行并插入有效词语
                if (!word.isEmpty()) {
                    insert(word);
                }
            }
        }
    }

    /**
     * 将集合中的字符串加载到字典中，自动去重并忽略空字符串。
     *
     * @param collection 包含待加载单词的字符串集合，可能包含重复项和空白字符串
     * @return 无返回值
     */
    public void loadDictionaryByCollection(Collection<String> collection) {
        /**
         * 使用流处理集合中的单词：
         * 1. 通过distinct()去除重复项
         * 2. 对每个单词执行以下操作：
         *    - 去除前后空白字符
         *    - 仅当非空时插入字典
         */
        collection.stream().distinct().forEach(
                word -> {
                    word = word.trim();
                    if (!word.isEmpty()) {
                        insert(word);
                    }
                }
        );
    }


}

/**
 * 中文分词器主类，基于字典树实现正向最大匹配算法
 * 支持加载词典并执行分词操作，处理中文文本切分
 */
public class SensitiveWordGreenServer {
    // 底层使用的字典树实例
    private Trie trie;

    /**
     * 初始化包含空字典树的分词器
     * 创建默认配置的分词器实例
     */
    public SensitiveWordGreenServer() {
        this.trie = new Trie();
    }

    /**
     * 加载外部词典到分词器
     * 将指定文件中的词语加载到字典树中
     *
     * @param filePath 词典文件路径（相对路径或绝对路径）
     * @throws IOException 文件读取异常时抛出
     */
    public void loadDictionary(String filePath) throws IOException {
        trie.loadDictionaryByFile(filePath);
    }

    /**
     * 将指定的字符串集合加载到字典树（Trie）结构中，用于构建或更新字典数据
     *
     * @param collection 待加载的字符串集合，元素为字典词条
     * @throws IOException 当底层字典树加载过程中发生I/O异常时抛出
     */
    public void loadDictionary(Collection<String> collection){
        trie.loadDictionaryByCollection(collection);
    }

    /**
     * 对输入文本进行分词处理
     * 使用正向最大匹配算法实现中文分词
     * @param text 待分词的原始文本（支持Unicode中文字符）
     * @return 按分词结果顺序排列的字符串列表
     *         每个元素为一个词语或单字
     */
    public List<String> green(String text) {
        List<String> result = new ArrayList<>();
        if (text == null || text.isEmpty()) {
            return result;
        }

        int n = text.length();
        int start = 0;

        // 主循环：逐字符处理剩余文本
        while (start < n) {
            TrieNode node = trie.root;
            int end = start;
            int lastMatchEnd = -1;

            // 内层循环：从当前位置尝试最长匹配
            while (end < n) {
                char c = text.charAt(end);
                // 沿字典树路径向下查找
                if (node.children.containsKey(c)) {
                    node = node.children.get(c);
                    end++;

                    // 记录当前有效词语的结束位置
                    if (node.isEnd) {
                        lastMatchEnd = end;
                    }
                } else {
                    break;
                }
            }

            // 根据匹配结果选择分词策略
            if (lastMatchEnd != -1) {
                // 使用最长匹配结果
                String word = text.substring(start, lastMatchEnd);
                result.add(word);
                start = lastMatchEnd;
            }
            start++;
        }

        return result;
    }

    /**
     * 测试用例入口函数
     * 演示分词器的基本使用流程
     * @param args 命令行参数（未使用）
     */
    public static void main(String[] args) {
        SensitiveWordGreenServer segmenter = new SensitiveWordGreenServer();

        try {
            // 加载外部词典文件
            segmenter.loadDictionary("dictionary.txt");

            // 测试分词处理
            String text = "他说的大麻真的是冰毒吗";
            List<String> result = segmenter.green(text);

            // 输出处理结果
            System.out.println("输入文本: " + text);
            System.out.print("分词结果: ");
            for (String word : result) {
                System.out.print("[" + word + "] ");
            }
        } catch (IOException e) {
            // 异常处理：文件加载失败
            System.err.println("加载词典失败: " + e.getMessage());
        }
    }
}
